﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace CoreShop.PrintService
{
    /// <summary>
    /// 对齐方式
    /// </summary>
    public enum eTextAlignMode
    {
        Left = 0,
        Middle = 1,
        Right = 2
    }

    /// <summary>
    /// ESC指令创建类
    /// </summary>
    public class PrinterBase
    {
        #region 指令定义 

        private static Byte[] Const_Init = new byte[] { 0x1B, 0x40,
            0x20, 0x20, 0x20, 0x0A,
            0x1B, 0x64,0x10};

        //设置左边距
        private const string Const_SetLeft = "1D 4C ";


        //设置粗体
        private const string Const_SetBold = "1B 45 ";
        private const string Const_Bold_YES = "01";
        private const string Const_Bold_NO = "00";


        //设置对齐方式
        private const string Const_SetAlign = "1B 61 ";
        private const string Const_Align_Left = "30";
        private const string Const_Align_Middle = "31";
        private const string Const_Align_Right = "32";

        //设置字体大小,与 SetBigFont 不能同时使用
        private const string Const_SetFontSize = "1D 21 ";

        //设置是否大字体,等同于 SetFontSize = 2
        //private const string Const_SetBigFontBold = "1B 21 38";
        //private const string Const_SetBigFontNotBold = "1B 21 30";
        //private const string Const_SetCancelBigFont = "1B 21 00";

        /// <summary>
        /// 打印并走纸
        /// </summary>
        private static Byte[] Const_Cmd_Print = new byte[] { 0x1B, 0x4A, 0x18 };
        //走纸
        private const string Const_FeedForward = "1B 4A ";
        private const string Const_FeedBack = "1B 6A ";

        //切纸
        private static Byte[] Const_SetCut = new byte[] { 0x1D, 0x56, 0x30 };
        private static Byte[] Const_SetCutHalf = new byte[] { 0x1D, 0x56, 0x31 };

        //查询打印机状态
        private static Byte[] Const_QueryID = new byte[] { 0x1D, 0x67, 0x61 };

        //回复帧以 ID 开头 
        //private static string Const_ResponseQueryID = "ID";

        /// <summary>
        /// 设置图标的指令
        /// </summary>
        private static Byte[] Const_SetImageCommand = new Byte[] { 0x1B, 0x2A, 0x21 };

        /// <summary>
        /// 设置行间距为0
        /// </summary>
        private static Byte[] Const_Row0 = new Byte[] { 0x1b, 0x33, 0x00 };
        /// <summary>
        /// 设置行间距为默认值
        /// </summary>
        private static Byte[] Const_RowDefault = new Byte[] { 0x1b, 0x32 };

        /// <summary>
        /// 打印自测页
        /// </summary>
        private static Byte[] Const_Print_Test = new Byte[] { 0x12, 0x54 };

        /// <summary>
        /// 设置一维条码可读字符（HRI）打印位置,第三位
        /// n        打印位置
        /// 0，48    不打印
        /// 1，49    条码的上方
        /// 2，50    条码的下方
        /// 3，51    条码的上方和下方
        /// </summary>
        private static string Const_Print_BarcodeHRI_Below = "1D 48 02 ";
        /// <summary>
        /// 设置一维条码高度，第三位
        /// </summary>
        private static string Const_Print_Barcode_Higth = "1D 68 40 ";
        /// <summary>
        /// 设置一维条码宽度，第三位
        /// </summary>
        private static string Const_Print_Barcode_Width = "1D 77 ";
        /// <summary>
        /// 打印一维条码
        /// </summary>
        private static string Const_Print_Barcode = "1D 6B 49 ";

        /// <summary>
        /// 汉字模式
        /// </summary>
        private static Byte[] Const_Set_Chinese = new Byte[] { 0x1c, 0x26 };
        /// <summary>
        /// 取消汉字模式
        /// </summary>
        private static Byte[] Const_Reset_Ascll = new Byte[] { 0x1c, 0x2e };
        /// <summary>
        /// 切换UTF-8
        /// </summary>
        private static Byte[] Const_UTF8 = new Byte[] { 0x1b, 0x40, 0x1c, 0x26, 0x1b, 0x39, 0x01 };
        private static Byte[] Const_GBK = new Byte[] { 0x1b, 0x40, 0x1c, 0x26, 0x1b, 0x39, 0x00 };
        /// <summary>
        /// 打印机切换语言-阿拉伯语
        /// </summary>
        private static Byte[] Const_Lan_Arabic = new Byte[] { 0x1B, 0x74, 0x16 };
        /// <summary>
        /// Code Page 864, 切换阿拉伯语
        /// </summary>
        /// key-字符ASCLL码, value-代码页对应编码
        private static Dictionary<Int32, Int32> Const_Arabic_Page864 = new Dictionary<int, int> {
            { 0x00B0, 0x80 }, { 0x00B7, 0x81 }, { 0x2219, 0x82 }, { 0x221A, 0x83 },
            { 0x2592, 0x84 }, { 0x2500, 0x85 }, { 0x2502, 0x86 }, { 0x253C, 0x87 },
            { 0x2524, 0x88 }, { 0x252C, 0x89 }, { 0x251C, 0x8A }, { 0x2534, 0x8B },
            { 0x2510, 0x8C }, { 0x250C, 0x8D }, { 0x2514, 0x8E }, { 0x2518, 0x8F },
            { 0x03B2, 0x90 }, { 0x221E, 0x91 }, { 0x03C6, 0x92 }, { 0x00B1, 0x93 },
            { 0x00BD, 0x94 }, { 0x00BC, 0x95 }, { 0x2248, 0x96 }, { 0x00AB, 0x97 },
            { 0x00BB, 0x98 }, { 0xFEF7, 0x99 }, { 0xFEF8, 0x9A },
            { 0xFEFB, 0x9D }, { 0xFEFC, 0x9E },
            { 0x00A0, 0xA0 }, { 0x00AD, 0xA1 }, { 0xFE82, 0xA2 }, { 0x00A3, 0xA3 },
            { 0x00A4, 0xA4 }, { 0xFE84, 0xA5 }, { 0x20AC, 0xA7 },
            { 0xFE8E, 0xA8 }, { 0xFE8F, 0xA9 }, { 0xFE95, 0xAA }, { 0xFE99, 0xAB },
            { 0x060C, 0xAC }, { 0xFE9D, 0xAD }, { 0xFEA1, 0xAE }, { 0xFEA5, 0xAF },
            { 0x0660, 0xB0 }, { 0x0661, 0xB1 }, { 0x0662, 0xB2 }, { 0x0663, 0xB3 },
            { 0x0664, 0xB4 }, { 0x0665, 0xB5 }, { 0x0666, 0xB6 }, { 0x0667, 0xB7 },
            { 0x0668, 0xB8 }, { 0x0669, 0xB9 }, { 0xFED1, 0xBA }, { 0x061B, 0xBB },
            { 0xFEB1, 0xBC }, { 0xFEB5, 0xBD }, { 0xFEB9, 0xBE }, { 0x061F, 0xBF },
            { 0x00A2, 0xC0 }, { 0xFE80, 0xC1 }, { 0xFE81, 0xC2 }, { 0xFE83, 0xC3 },
            { 0xFE85, 0xC4 }, { 0xFECA, 0xC5 }, { 0xFE8B, 0xC6 }, { 0xFE8D, 0xC7 },
            { 0xFE91, 0xC8 }, { 0xFE93, 0xC9 }, { 0xFE97, 0xCA }, { 0xFE9B, 0xCB },
            { 0xFE9F, 0xCC }, { 0xFEA3, 0xCD }, { 0xFEA7, 0xCE }, { 0xFEA9, 0xCF },
            { 0xFEAB, 0xD0 }, { 0xFEAD, 0xD1 }, { 0xFEAF, 0xD2 }, { 0xFEB3, 0xD3 },
            { 0xFEB7, 0xD4 }, { 0xFEBB, 0xD5 }, { 0xFEBF, 0xD6 }, { 0xFEC1, 0xD7 },
            { 0xFEC5, 0xD8 }, { 0xFECB, 0xD9 }, { 0xFECF, 0xDA }, { 0x00A6, 0xDB },
            { 0x00AC, 0xDC }, { 0x00F7, 0xDD }, { 0x00D7, 0xDE }, { 0xFEC9, 0xDF },
            { 0x0640, 0xE0 }, { 0xFED3, 0xE1 }, { 0xFED7, 0xE2 }, { 0xFEDB, 0xE3 },
            { 0xFEDF, 0xE4 }, { 0xFEE3, 0xE5 }, { 0xFEE7, 0xE6 }, { 0xFEEB, 0xE7 },
            { 0xFEED, 0xE8 }, { 0xFEEF, 0xE9 }, { 0xFEF3, 0xEA }, { 0xFEBD, 0xEB },
            { 0xFECC, 0xEC }, { 0xFECE, 0xED }, { 0xFECD, 0xEE }, { 0xFEE1, 0xEF },
            { 0xFE7D, 0xF0 }, { 0x0651, 0xF1 }, { 0xFEE5, 0xF2 }, { 0xFEE9, 0xF3 },
            { 0xFEEC, 0xF4 }, { 0xFEF0, 0xF5 }, { 0xFEF2, 0xF6 }, { 0xFED0, 0xF7 },
            { 0xFED5, 0xF8 }, { 0xFEF5, 0xF9 }, { 0xFEF6, 0xFA }, { 0xFEDD, 0xFB },
            { 0xFED9, 0xFC }, { 0xFEF1, 0xFD }, { 0x25A0, 0xFE }
        };

        #endregion

        #region 常量定义

        /// <summary>
        /// 最大字体大小
        /// </summary>
        public const Int32 Const_MaxFontSize = 8;
        /// <summary>
        /// 最大走纸距离
        /// </summary>
        public const Int32 Const_MaxFeedLength = 5000;

        /// <summary>
        /// 最大高宽
        /// </summary>
        public const Int32 Const_MaxImageLength = 480;

        /// <summary>
        /// 每次通信最多打印的行数
        /// </summary>
        public const Int32 Const_OncePrintRowCount = 24;

        public const Int32 Const_BrightnessGate = 100;

        #endregion

        #region 需要子类覆盖的函数

        /// <summary>
        /// 打印机是否已经初始化,必须由子类实现
        /// </summary>
        protected bool m_Inited = false;


        /// <summary>
        /// 发送指令
        /// </summary>
        /// <param name="cmd"></param>
        /// <returns></returns>
        protected virtual bool SendCommand(Byte[] cmd)
        {
            if (!m_Inited)
            {
                return false;
            }
            return true;
        }

        #endregion

        #region 内部函数

        /// <summary>
        /// 发送文本格式的指令
        /// </summary>
        /// <param name="cmd"></param>
        /// <returns></returns>
        protected bool SendCommand(string hexstrcmd)
        {
            if (!m_Inited)
            {
                return false;
            }

            byte[] mybyte = null;
            bool bl = Processor.HexStringToBytes(hexstrcmd, out mybyte);
            bl = SendCommand(mybyte);
            return bl;
        }
        #endregion

        #region 公开函数

        /// <summary>
        /// 打印文本.在调用本函数之前必须先调用正确的 设置字体、左边距
        /// </summary>
        /// <param name="content"></param>
        /// <returns></returns>
        public bool PrintText(string content)
        {
            byte[] bytes = null;
            if (content.Length < 1)
            {
                content = "  ";
            }

            if (content[content.Length - 1] != (char)0x0D &&
                content[content.Length - 1] != (char)0x0A)
            {
                content = content + (char)0x0A;
            }

            bytes = Processor.StringToBytes(content);
            return SendCommand(bytes);
        }

        public bool PrintText(byte[] content)
        {
            byte[] bts = new byte[content.Length + 1];
            Array.Copy(content, 0, bts, 0, content.Length);
            bts[bts.Length - 1] = 0x0A;
            return SendCommand(bts);
        }

        /// <summary>
        /// 设置对齐方式
        /// </summary>
        /// <param name="left"></param>
        /// <returns></returns>
        public bool SetAlignMode(eTextAlignMode alignmode)
        {
            string code = string.Empty;
            switch (alignmode)
            {
                case eTextAlignMode.Left:
                    code = Const_Align_Left;
                    break;
                case eTextAlignMode.Middle:
                    code = Const_Align_Middle;
                    break;
                case eTextAlignMode.Right:
                    code = Const_Align_Right;
                    break;
                default:
                    code = Const_Align_Left;
                    break;
            }

            //注意：先低字节后高字节 
            string str = Const_SetAlign + code;
            bool bl = SendCommand(str);
            return bl;
        }

        /// <summary>
        /// 设置左边距
        /// </summary>
        /// <param name="left"></param>
        /// <returns></returns>
        public bool SetLeft(int left)
        {
            //注意：先低字节后高字节
            string hexstr = left.ToString("X4");
            string str = Const_SetLeft + hexstr.Substring(2, 2) + hexstr.Substring(0, 2);
            bool bl = SendCommand(str);
            return bl;
        }

        /// <summary>
        /// 设置粗体
        /// </summary>
        /// <param name="bold"></param>
        /// <returns></returns>
        public bool SetBold(bool bold)
        {
            //注意：先低字节后高字节
            string str = string.Empty;
            if (bold)
            {
                str = Const_SetBold + Const_Bold_YES;
            }
            else
            {
                str = Const_SetBold + Const_Bold_NO;
            }
            bool bl = SendCommand(str);
            return bl;
        }

        /// <summary>
        /// 切纸
        /// </summary>
        /// <returns></returns>
        public bool Cut()
        {
            bool bl = SendCommand(Const_SetCut);
            return bl;
        }

        public bool TestPrintBitmap()
        {

            StringBuilder cmdb = new StringBuilder();
            cmdb.Append("1D2A 08 08");
            //64*64 点，共4096点,共512字节
            for (Int32 i = 0; i < 512; i++)
            {
                cmdb.Append("FF");
            }

            string cmd = cmdb.ToString();
            bool bl = SendCommand(cmd);
            bl = SendCommand("1D 2F 30");
            return bl;
        }
        /// <summary>
        /// 打印图片
        /// </summary>
        /// <param name="bitmap"></param>
        /// <returns></returns>
        public bool PrintBitmapBytes(Byte[] imgbitarray, Int32 width, Int32 height)
        {
            Int32 bytes = width * height / 8;
            //检查是否尺寸符合要求
            if (width > Const_MaxImageLength || height > Const_MaxFeedLength ||
                width < 1 || height < 1 ||
                imgbitarray == null)
            {
                return false;
            }

            //每次获取24行的数据进行发送,这24行的字节数
            Int32 blockbytes = width * Const_OncePrintRowCount / 8;
            if (blockbytes < 1)
            {
                return false;
            }

            bool bl = false;

            //换行
            Byte[] newline = new Byte[] { 0x0A };

            //一共需要发送的块数量
            Int32 blocks = imgbitarray.Length / blockbytes;

            //每次发送的数据字节数 = 1B 2A 21 2字节长度 +　数据内容
            Byte[] cmdbytes = new Byte[5 + blockbytes];
            //指令
            Array.Copy(Const_SetImageCommand, cmdbytes, 3);
            //数据长度,即 每行的点数
            Processor.Int16ToBytes(width, ref cmdbytes, 3);
            //打印前设置间距为0
            bl = SendCommand(Const_Row0);
            //数据内容
            for (Int32 blockidx = 0; blockidx < blocks; ++blockidx)
            {
                Array.Copy(imgbitarray, blockidx * blockbytes, cmdbytes, 5, blockbytes);
                //发送当前指令
                bl = SendCommand(cmdbytes);
                if (!bl) return false;
                //休眠20毫秒
                Thread.Sleep(20);

                //发送 打印指令
                bl = SendCommand(Const_Cmd_Print);
                //改为发送换行指令
                //bl = SendCommand("0A");
            }
            //图片传输结束，将间距恢复
            bl = SendCommand(Const_RowDefault);
            bl = SendCommand("0A");

            return bl;
        }

        /// <summary>
        /// 走纸
        /// </summary>
        /// <param name="length"></param>
        /// <returns></returns>
        public bool Feed(int length)
        {
            if (length < 1)
                length = 1;
            if (length > Const_MaxFeedLength)
            {
                length = Const_MaxFeedLength;
            }
            string len = length.ToString("X2");
            len = Const_FeedForward + len;
            bool bl = SendCommand(len);
            return bl;
        }

        /// <summary>
        /// 设置字体大小.本函数不可与SetBigFont同时使用
        /// </summary>
        /// <param name="sizerate">大小倍率,取值范围 1 - 8</param>
        /// <returns></returns>
        public bool SetFontSize(Int32 sizerate)
        {
            if (sizerate < 1)
            {
                sizerate = 1;
            }

            if (sizerate > Const_MaxFontSize)
            {
                sizerate = Const_MaxFontSize;
            }
            sizerate--;
            string sizecodestr = Const_SetFontSize + sizerate.ToString("X1") + sizerate.ToString("X1");
            bool bl = SendCommand(sizecodestr);
            return bl;
        }

        /// <summary>
        /// 打印阿拉伯语
        /// </summary>
        /// <returns></returns>
        public bool PrintArabic(string data)
        {
            bool bl = SendCommand(Const_Lan_Arabic);

            char[] codes = data.ToCharArray();
            if (codes == null || codes.Length < 1)
            {
                return false;
            }
            List<string> bytecode = new List<string>();
            Int32 asc = 0;
            foreach (char code in codes)
            {
                if (Const_Arabic_Page864.ContainsKey((Int32)code))
                {
                    asc = Const_Arabic_Page864[(Int32)code];
                }
                else if ((Int32)code > 255)
                {
                    //0x3f:?
                    asc = 0x3f;
                }
                else
                {
                    asc = (Int32)code;
                }
                bytecode.Add(Convert.ToString(asc, 16));
            }

            string liststring = Processor.ListToString(bytecode, ' ');
            if (liststring.Length < 1)
            {
                liststring = "  ";
            }

            byte[] bytes = Processor.HexStringToBytes(liststring);
            byte[] endbyte = new byte[] { 0x0a };
            Array.Resize(ref bytes, bytes.Length + endbyte.Length);
            endbyte.CopyTo(bytes, bytes.Length - endbyte.Length);
            bl = SendCommand(bytes);

            return bl;
        }

        /// <summary>
        /// 打印Unicode
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public bool PrintUnicode(string str)
        {
            bool bl = false;
            //bl = SendCommand(Const_UTF8);
            //char[] chars = str.ToCharArray();
            //Array.Reverse(chars);
            //str = new string(chars);
            Byte[] bytes = Processor.StringToBytes(str, System.Text.Encoding.UTF8);
            bl = SendCommand(bytes);
            bl = SendCommand("0A");
            return bl;
        }

        /// <summary>
        /// 打印测试页
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public bool PrintTestPage()
        {
            bool bl = false;
            bl = SendCommand(Const_Print_Test);
            bl = SendCommand("0A");
            return bl;
        }

        /// <summary>
        /// 汉字模式
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public bool ChineseMode()
        {
            bool bl = false;
            bl = SendCommand(Const_Set_Chinese);
            return bl;
        }

        /// <summary>
        /// ASCLL模式
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public bool ASCLLMode()
        {
            bool bl = false;
            bl = SendCommand(Const_Reset_Ascll);
            return bl;
        }

        /// <summary>
        /// 打印条码
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public bool PrintBarcode(string str)
        {
            bool bl = false;
            Byte[] bytes = Processor.StringToBytes(str, System.Text.Encoding.UTF8);
            Byte lengthbyte = (Byte)str.Length;
            string hex = Processor.BytesToHexString(bytes);
            string lengthhex = Processor.ByteToHexString(lengthbyte);
            bl = SendCommand(Const_Print_BarcodeHRI_Below);
            bl = SendCommand(Const_Print_Barcode_Higth);
            string widthhex = str.Length > 12 ? "01" : "02";
            bl = SendCommand(Const_Print_Barcode_Width + widthhex);
            bl = SendCommand(Const_Print_Barcode + lengthhex + hex);
            bl = SendCommand("0A");
            return bl;
        }

        public bool uniprintBarcodes(int m, byte[] content)
        {
            byte[] data3 = { 0x1D, 0x66, 0 };
            SendCommand(data3);

            byte[] data0 = { 0x1D, 0x48, 1 };
            SendCommand(data0);

            byte[] data1 = { 0x1D, 0x77, 3 };
            SendCommand(data1);

            byte[] data2 = { 0x1D, 0x68, 162 };
            SendCommand(data2);

            byte[] data = { 0x1D, 0x6B, (byte)m };
            byte[] send = new byte[3 + content.Length + 1];
            Array.Copy(data, 0, send, 0, 3);
            Array.Copy(content, 0, send, 3, content.Length);
            return SendCommand(send);
        }

        #endregion                 
    }
}
